package org.tio.http.common;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;

/**
 *
 * @author tanyaowu
 * 2017年8月4日 上午9:41:12
 */
public class HttpResponseEncoder {
	public static enum Step {
		firstline, header, body
	}

	private static Logger log = LoggerFactory.getLogger(HttpResponseEncoder.class);

	public static final int MAX_HEADER_LENGTH = 20480;
	
	private static final byte[] rnBytes = "\r\n".getBytes();
	private static final byte[] http1_1Bytes = "HTTP/1.1 ".getBytes();
	
	private static final Map<String, byte[]> bytesMap = new HashMap<>(512);
	
	private static byte[] getBytes(String str) {
		byte[] ret = bytesMap.get(str);
		if (ret == null) {
			try {
				ret = str.getBytes("utf-8");
			} catch (UnsupportedEncodingException e) {
				log.error(e.toString(), e);
			}
			bytesMap.put(str, ret);
		}
		return ret;
	}

	private static int add(byte[][] list, byte[] bs, int index) {
		list[index] = bs;
		return bs.length;
	}
	
	/**
	 *
	 * @param httpResponse
	 * @param groupContext
	 * @param channelContext
	 * @param skipCookie true: 忽略掉cookie部分的编码
	 * @return
	 * @author tanyaowu
	 */
	public static ByteBuffer encode(HttpResponse httpResponse, GroupContext groupContext, ChannelContext channelContext, boolean skipCookie) {
		byte[] encodedBytes = httpResponse.getEncodedBytes();
		if (encodedBytes != null) {
			ByteBuffer ret = ByteBuffer.wrap(encodedBytes);
			ret.position(ret.limit());
			return ret;
		}

		int bodyLength = 0;
		byte[] body = httpResponse.getBody();
		if (body != null) {
			bodyLength = body.length;
		}

//		StringBuilder sb = new StringBuilder(512);
		Map<String, String> headers = httpResponse.getHeaders();
		headers.put(HttpConst.ResponseHeaderKey.Content_Length, bodyLength + "");
		
		int size = 4;  //第一行有4个
		if (headers != null && headers.size() > 0) {
			size += (headers.size() * 3);
		}
		if (!skipCookie) {
			if (httpResponse.getCookies() != null) {
				size += (httpResponse.getCookies().size() * 3);
			}
		}
		size++;  //最后有一个空行
		
		byte[][] headerBytesList = new byte[size][];
		int index = 0;
		int headerBytesCount = 0;

		HttpResponseStatus httpResponseStatus = httpResponse.getStatus();
//		sb.append("HTTP/1.1 ").append(httpResponseStatus.getStatus()).append(" ").append(httpResponseStatus.getDescription()).append("\r\n");
		headerBytesCount += add(headerBytesList, http1_1Bytes, index++);
		headerBytesCount += add(headerBytesList, getBytes(httpResponseStatus.getStatus() + " "), index++);
		headerBytesCount += add(headerBytesList, getBytes(httpResponseStatus.getDescription()), index++);
		headerBytesCount += add(headerBytesList, rnBytes, index++);

		
		if (headers != null && headers.size() > 0) {
			
			Set<Entry<String, String>> headerSet = headers.entrySet();
			try {
				for (Entry<String, String> entry : headerSet) {
//					sb.append(entry.getKey()).append(":").append(entry.getValue()).append("\r\n");
					headerBytesCount += add(headerBytesList, getBytes(entry.getKey() + ":"), index++);
					headerBytesCount += add(headerBytesList, entry.getValue().getBytes(httpResponse.getCharset()), index++);
					headerBytesCount += add(headerBytesList, rnBytes, index++);
				}
			} catch (UnsupportedEncodingException e) {
				log.error(e.toString(), e);
			}
		}

		if (!skipCookie) {
			//处理cookie
			if (httpResponse.getCookies() != null) {
				try {
					for (Cookie cookie : httpResponse.getCookies()) {
//						sb.append(HttpConst.ResponseHeaderKey.Set_Cookie).append(":");
//						sb.append(cookie.toString());
//						sb.append("\r\n");
						
						headerBytesCount += add(headerBytesList, getBytes(HttpConst.ResponseHeaderKey.Set_Cookie + ":"), index++);
						headerBytesCount += add(headerBytesList, cookie.toString().getBytes(httpResponse.getCharset()), index++);
						headerBytesCount += add(headerBytesList, rnBytes, index++);
						
						if (log.isInfoEnabled()) {
							log.info("{}, path:{}, set-cookie:{}", channelContext, httpResponse.getHttpRequest().getRequestLine().getPathAndQuery(), cookie.toString());
						}
					}
				} catch (UnsupportedEncodingException e) {
					log.error(e.toString(), e);
				}
			}
		}

//		sb.append("\r\n");
		headerBytesCount += add(headerBytesList, rnBytes, index++);

//		byte[] headerBytes = null;
//		try {
//			String headerString = sb.toString();
//			if (log.isInfoEnabled()) {
//				log.info("{}, response header:{}", channelContext, headerString);
//			}
//			httpResponse.setHeaderString(headerString);
//			headerBytes = headerString.getBytes(httpResponse.getCharset());
//		} catch (Throwable e) {
//			throw new RuntimeException(e);
//		}

		ByteBuffer buffer = ByteBuffer.allocate(headerBytesCount + bodyLength);
		for (byte[] bytes : headerBytesList) {
			buffer.put(bytes);
		}
//		buffer.put(headerBytes);

		if (bodyLength > 0) {
			buffer.put(body);
		}
		return buffer;
	}

	/**
	 * @param args
	 *
	 * @author tanyaowu
	 * 2017年2月22日 下午4:06:42
	 *
	 */
	public static void main(String[] args) {

	}

	/**
	 * 解析请求头的每一行
	 * @param line
	 * @return
	 *
	 * @author tanyaowu
	 * 2017年2月23日 下午1:37:58
	 *
	 */
	public static KeyValue parseHeaderLine(String line) {
		KeyValue keyValue = new KeyValue();
		int p = line.indexOf(":");
		if (p == -1) {
			keyValue.setKey(line);
			return keyValue;
		}

		String name = line.substring(0, p).trim();
		String value = line.substring(p + 1).trim();

		keyValue.setKey(name);
		keyValue.setValue(value);

		return keyValue;
	}

	/**
	 *
	 *
	 * @author tanyaowu
	 */
	public HttpResponseEncoder() {

	}

}
